package synchronization

import (
	"fmt"
	"time"

	"github.com/onflow/flow-go/config"
	core "github.com/onflow/flow-go/module/chainsync"
)

type Config struct {
	PollInterval time.Duration
	ScanInterval time.Duration
}

func DefaultConfig() *Config {
	scanInterval := 2 * time.Second
	pollInterval := time.Duration(core.DefaultQueuedHeightMultiplicity) * scanInterval
	return &Config{
		PollInterval: pollInterval,
		ScanInterval: scanInterval,
	}
}

type OptionFunc func(*Config)

// WithPollInterval sets a custom interval at which we scan for poll items
func WithPollInterval(interval time.Duration) OptionFunc {
	return func(cfg *Config) {
		cfg.PollInterval = interval
	}
}

// WithScanInterval sets a custom interval at which we scan for pending items
// and batch them for requesting.
func WithScanInterval(interval time.Duration) OptionFunc {
	return func(cfg *Config) {
		cfg.ScanInterval = interval
	}
}

// spamProbabilityMultiplier is used to convert probability factor to an integer as well as a maximum value for the
// random number that can be generated by the random number generator.
const spamProbabilityMultiplier = 1000

// SpamDetectionConfig contains configuration parameters for spam detection for different message types.
// The probability of creating a misbehavior report for a message of a given type is calculated differently for different
// message types.
// MisbehaviourReports are generated for two reasons:
//  1. A malformed message will always produce a MisbehaviourReport, to notify ALSP of *unambiguous* spam.
//  2. A correctly formed message may produce a MisbehaviourReport probabilistically, to notify ALSP of *ambiguous* spam.
//     This effectively tracks the load associated with a particular sender, for this engine, and, on average,
//     reports message load proportionally as misbehaviour to ALSP.
type SpamDetectionConfig struct {

	// batchRequestBaseProb is the base probability in [0,1] that's used in creating the final probability of creating a
	// misbehavior report for a BatchRequest message. This is why the word "base" is used in the name of this field,
	// since it's not the final probability and there are other factors that determine the final probability.
	// The reason for this is that we want to increase the probability of creating a misbehavior report for a large batch.
	batchRequestBaseProb float32

	// syncRequestProb is the probability in [0,1] of creating a misbehavior report for a SyncRequest message.
	syncRequestProb float32

	// rangeRequestBaseProb is the base probability in [0,1] that's used in creating the final probability of creating a
	// misbehavior report for a RangeRequest message. This is why the word "base" is used in the name of this field,
	// since it's not the final probability and there are other factors that determine the final probability.
	// The reason for this is that we want to increase the probability of creating a misbehavior report for a large range.
	rangeRequestBaseProb float32
}

func NewSpamDetectionConfig() (*SpamDetectionConfig, error) {
	flowConfig, err := config.DefaultConfig()
	if err != nil {
		return nil, fmt.Errorf("failed to read default config: %w", err)
	}

	return &SpamDetectionConfig{
		// see config/default-config.yml for more information on the following fields
		batchRequestBaseProb: flowConfig.NetworkConfig.SyncEngine.BatchRequestBaseProb,
		syncRequestProb:      flowConfig.NetworkConfig.SyncEngine.SyncRequestProb,
		rangeRequestBaseProb: flowConfig.NetworkConfig.SyncEngine.RangeRequestBaseProb,
	}, nil
}
